// --- Copyright (c) 2006-2008 Stefan Kyntchev ---
// This software is written and copyrighted by Stefan Kyntchev 
// and BeyondPod Team members. All rights are reserved.
// Author contact: support@beyondpod.mobi
// ------------------------------------------------------------------------
// This file is part of BeyondPod RSS Feed Reader and Podcast manager
// (www.codeplex.com/BeyondPod) 
// BeyondPod is free software: you can redistribute it and/or modify 
// it under the terms of the GNU General Public License as published by 
// the Free Software Foundation, either version 3 of the License, or 
// (at your option) any later version. 
//  
// BeyondPod is distributed in the hope that it will be useful, 
// but WITHOUT ANY WARRANTY; without even the implied warranty of 
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the 
// GNU General Public License for more details. 
//  
// You should have received a copy of the GNU General Public License
// along with BeyondPod. If not, see <http://www.gnu.org/licenses/>
// ------------------------------------------------------------------------
// COPYRIGHT NOTICE: This copyright notice may NOT be removed, obscured or modified 
// without written consent from the author.
// --- End copyright notice --- 


using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Text;

namespace svs.Mobile.BeyondMedia.FeedCore.Helpers
{
	/// <summary>
	/// Summary description for ProcessHelper.
	/// </summary>
	public class ProcessHelper
	{
		private static Process _myProcess;
		private readonly int _baseAddress;
		private readonly IntPtr _handle;
		private readonly string _processName;
		private readonly int _threadCount;

		#region Constructors

		private ProcessHelper(IntPtr id, string procname, int threadcount, int baseaddress)
		{
			_handle = id;
			_processName = procname;
			_threadCount = threadcount;
			_baseAddress = baseaddress;
		}

		#endregion

		#region Properties

		public int BaseAddress
		{
			get { return _baseAddress; }
		}

		public int ThreadCount
		{
			get { return _threadCount; }
		}

		public int Handle
		{
			get { return (int) _handle; }
		}

		public string ProcessName
		{
			get { return _processName; }
		}

		public int BaseAddess
		{
			get { return _baseAddress; }
		}

		public static ProcessHelper MyProcess
		{
			get
			{
				List<ProcessHelper> procs = GetProcesses();

				foreach (ProcessHelper proc in procs)
				{
					if (proc.Handle == MyProcessID)
						return proc;
				}

				return null;
			}
		}

		public override string ToString()
		{
			return _processName;
		}

		#endregion

		#region Public Methods

		public static int MyProcessID
		{
			get
			{
				if (_myProcess == null)
				{
					_myProcess = Process.GetCurrentProcess();
				}

				return _myProcess.Id;
			}
		}

		public void Kill()
		{
			IntPtr hProcess;

			hProcess = OpenProcess(PROCESS_TERMINATE, false, Handle);

			if (hProcess != (IntPtr) INVALID_HANDLE_VALUE)
			{
				TerminateProcess(hProcess, 0);
				CloseHandle(hProcess);
			}
		}

		public static void KilExistingInstances()
		{
			List<ProcessHelper> procs = GetProcesses();

			ProcessHelper toKill = null;


			foreach (ProcessHelper proc in procs)
			{
				if (string.Compare(proc._processName, "BeyondPod.exe", true) == 0 && proc.Handle != MyProcessID)
				{
					toKill = proc;
					break;
				}
			}

			if (toKill != null)
			{
				toKill.Kill();
			}
		}

		public static List<ProcessHelper> GetProcesses()
		{
			List<ProcessHelper> procList = new List<ProcessHelper>(20);
			IntPtr handle = IntPtr.Zero;

			try
			{
				handle = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS | TH32CS_SNAPNOHEAPS, 0);

				if ((int) handle > 0)
				{
					PROCESSENTRY32 peCurrent;
					PROCESSENTRY32 pe32 = new PROCESSENTRY32();

					//Get byte array to pass to the API calls
					byte[] peBytes = pe32.ToByteArray();

					//Get the first process
					int retval = Process32First(handle, peBytes);

					while (retval == 1)
					{
						//Convert bytes to the class
						peCurrent = new PROCESSENTRY32(peBytes);

						//New instance
						procList.Add(new ProcessHelper(new IntPtr((int) peCurrent.PID), peCurrent.Name, (int) peCurrent.ThreadCount,
						                               (int) peCurrent.BaseAddress));

						retval = Process32Next(handle, peBytes);
					}
				}
			}
			finally
			{
				if ((int) handle > 0)
					CloseToolhelp32Snapshot(handle);
			}

			return procList;
		}

		#endregion

		#region PROCESSENTRY32 implementation

		//		typedef struct tagPROCESSENTRY32 
		//		{ 
		//			DWORD dwSize; 
		//			DWORD cntUsage; 
		//			DWORD th32ProcessID; 
		//			DWORD th32DefaultHeapID; 
		//			DWORD th32ModuleID; 
		//			DWORD cntThreads; 
		//			DWORD th32ParentProcessID; 
		//			LONG pcPriClassBase; 
		//			DWORD dwFlags; 
		//			TCHAR szExeFile[MAX_PATH]; 
		//			DWORD th32MemoryBase;
		//			DWORD th32AccessKey;
		//		} PROCESSENTRY32;

		private class PROCESSENTRY32
		{
			// constants for structure definition
			private const int AccessKeyOffset = 560;
			private const int DefaultHeapIDOffset = 12;
			private const int dwFlagsOffset = 32;
			private const int ExeFileOffset = 36;
			private const int MAX_PATH = 260;
			private const int MemoryBaseOffset = 556;
			private const int ModuleIDOffset = 16;
			private const int ParentProcessIDOffset = 24;
			private const int PriClassBaseOffset = 28;
			private const int ProcessIDOffset = 8;
			private const int Size = 564;
			private const int SizeOffset = 0;
			private const int ThreadsOffset = 20;
			private const int UsageOffset = 4;
			public readonly uint cntThreads;
			public readonly string szExeFile;
			public readonly uint th32MemoryBase;
			public readonly uint th32ProcessID;
			public uint cntUsage;
			public uint dwFlags;
			public uint dwSize;
			public long pcPriClassBase;
			public uint th32AccessKey;
			public uint th32DefaultHeapID;
			public uint th32ModuleID;
			public uint th32ParentProcessID;

			//Default constructor
			public PROCESSENTRY32()
			{
			}

			// create a PROCESSENTRY instance based on a byte array		
			public PROCESSENTRY32(byte[] aData)
			{
				dwSize = GetUInt(aData, SizeOffset);
				cntUsage = GetUInt(aData, UsageOffset);
				th32ProcessID = GetUInt(aData, ProcessIDOffset);
				th32DefaultHeapID = GetUInt(aData, DefaultHeapIDOffset);
				th32ModuleID = GetUInt(aData, ModuleIDOffset);
				cntThreads = GetUInt(aData, ThreadsOffset);
				th32ParentProcessID = GetUInt(aData, ParentProcessIDOffset);
				pcPriClassBase = GetUInt(aData, PriClassBaseOffset);
				dwFlags = GetUInt(aData, dwFlagsOffset);
				szExeFile = GetString(aData, ExeFileOffset, MAX_PATH);
				th32MemoryBase = GetUInt(aData, MemoryBaseOffset);
				th32AccessKey = GetUInt(aData, AccessKeyOffset);
			}

			#region Helper conversion functions

			// utility:  get a uint from the byte array
			private static uint GetUInt(byte[] aData, int Offset)
			{
				return BitConverter.ToUInt32(aData, Offset);
			}

			// utility:  set a uint int the byte array
			private static void SetUInt(byte[] aData, int Offset, int Value)
			{
				byte[] buint = BitConverter.GetBytes(Value);
				Buffer.BlockCopy(buint, 0, aData, Offset, buint.Length);
			}

			// utility:  get a unicode string from the byte array
			private static string GetString(byte[] aData, int Offset, int Length)
			{
				String sReturn = Encoding.Unicode.GetString(aData, Offset, Length);
				return sReturn;
			}

			#endregion

			// create an initialized data array

			public string Name
			{
				get { return szExeFile.Substring(0, szExeFile.IndexOf('\0')); }
			}

			public ulong PID
			{
				get { return th32ProcessID; }
			}

			public ulong BaseAddress
			{
				get { return th32MemoryBase; }
			}

			public ulong ThreadCount
			{
				get { return cntThreads; }
			}

			public byte[] ToByteArray()
			{
				byte[] aData;
				aData = new byte[Size];
				//set the Size member
				SetUInt(aData, SizeOffset, Size);
				return aData;
			}
		}

		#endregion

		#region PInvoke declarations

		private const int INVALID_HANDLE_VALUE = -1;
		private const int PROCESS_TERMINATE = 1;
		private const int TH32CS_SNAPNOHEAPS = 0x40000000;
		private const int TH32CS_SNAPPROCESS = 0x00000002;

		[DllImport("toolhelp.dll")]
		public static extern IntPtr CreateToolhelp32Snapshot(uint flags, uint processid);

		[DllImport("toolhelp.dll")]
		public static extern int CloseToolhelp32Snapshot(IntPtr handle);

		[DllImport("toolhelp.dll")]
		public static extern int Process32First(IntPtr handle, byte[] pe);

		[DllImport("toolhelp.dll")]
		public static extern int Process32Next(IntPtr handle, byte[] pe);

		[DllImport("coredll.dll")]
		private static extern IntPtr OpenProcess(int flags, bool fInherit, int PID);

		[DllImport("coredll.dll")]
		private static extern bool TerminateProcess(IntPtr hProcess, uint ExitCode);

		[DllImport("coredll.dll")]
		private static extern bool CloseHandle(IntPtr handle);

		[DllImport("coredll.dll")]
		private static extern IntPtr GetCurrentProcessId();

		#endregion
	}
}